/*
 * ell.h
 *
 *  Created on: Jun 29, 2011
 *      Author: Zhang
 */

#ifndef ELL_H_
#define ELL_H_
#include <mpi.h>
#include <string.h>
#include <stdlib.h>

template<typename T>
struct ELL
{
	//number of columns
	int  colsNum;

	//number of rows
	int  rowsNum;

	//column indices
	int* cols;

	//non-zero elements
	T* elements;

	static void free(ELL<T>* o){
		free(o->cols);
		free(o->elements);
		delete(o);
	}

	static void sendTo(ELL<T>* obj, int dst);
	void IsendTo(ELL<T>* obj, int dst);

	static ELL<T>* recvFrom(int src);
	ELL<T>* recvFrom(ELL<T>* obj, int src);

#ifdef __CUDACC__
	static void copyToDevice(ELL<T>* obj);
#endif

};


#ifdef __CUDACC__

template<class T>
void ELL<T>::copyToDevice(ELL<T>* obj){

	long long num = obj->colsNum * obj->rowsNum;

	int* pCols;
	cudaMalloc((void**)&pCols, sizeof(int) * num);
	cudaMemCopy(pCols, obj->cols, sizeof(int) * num, cudaMemcpyHostToDevice);

	T* pEle;
	cudaMalloc((void**)&pEle, sizeof(T) * num);
	cudaMemCopy(pEle, obj->elements, sizeof(T) * num, cudaMemcpyHostToDevice);

	//construct a temperal ELL<T> struct to copy data
	ELL<T> tempELL;
	tempELL.rowsNum = obj->rowsNum;
	tempELL.colsNum = obj->colsNum;
	tempELL.cols = pCols;
	tempELL.elements = pEle;

	ELL<T>* d_ELL;
	cudaMalloc((void**)&d_ELL, sizeof(ELL<T>));
	cudaMemCopy(d_ELL, &tempELL, sizeof(ELL<T>), cudaMemcpyHostToDevice);

}

#endif


template<class T>
void ELL<T>::sendTo(ELL<T>* obj, int dst){

	MPI::COMM_WORLD.Send(obj, sizeof(ELL<T>),MPI::BYTE, dst, 100);

	int num = obj->colsNum * obj->rowsNum;

	MPI::COMM_WORLD.Send(obj->cols, num, MPI::INT,dst, 101);

	//send by bytes since we cannot know what T is now.
	MPI::COMM_WORLD.Send(obj->elements, sizeof(T) * num, MPI::BYTE, dst, 102);
}

template<class T>
void ELL<T>::IsendTo(ELL<T>* obj, int dst){

	MPI::COMM_WORLD.Isend(obj, sizeof(ELL<T>),MPI::BYTE, dst, 100);

	int num = obj->colsNum * obj->rowsNum;

	MPI::COMM_WORLD.Isend(obj->cols, num, MPI::INT,dst, 101);

	//send by bytes since we cannot know what T is now.
	MPI::COMM_WORLD.Isend(obj->elements, sizeof(T) * num, MPI::BYTE, dst, 102);

}

template<class T>
ELL<T>* ELL<T>::recvFrom(ELL<T>* obj, int src){
	MPI::Status status;

	int ellSize = sizeof(ELL<T>);
	void* buf = (void*)malloc(sizeof(ELL<T>));

	MPI::COMM_WORLD.Recv(buf, ellSize, MPI::BYTE, src, 100, status);

	ELL<T>* e = (ELL<T>*)buf;

	int num = e->colsNum * e->rowsNum;

	//allocate memory
	e->cols = (int*)malloc(sizeof(int) * num);
	e->elements = (T*)malloc(sizeof(T) * num);

	MPI::COMM_WORLD.Recv(e->cols, num, MPI::INT, src, 101, status);
	MPI::COMM_WORLD.Recv(e->elements, num * sizeof(T), MPI::BYTE, src, 102, status);

	return e;
}


#endif /* ELL_H_ */
